﻿package we3d.loader 
{
	import flash.utils.setInterval;
	import flash.utils.clearInterval;
	import flash.events.Event;
	import flash.utils.ByteArray;
	
	import we3d.mesh.Face;
	import we3d.material.Surface;
	import we3d.scene.SceneObject;
	import we3d.loader.SceneLoader;
	import we3d.loader.TF;
	
	/**
	* Load WaveFront OBJ Files. <br/>
	* <br/>
	* The <code>loadedSurfaces</code> can be assigned before the file is loaded <br/>
	* <br/>
	* Support:<br/>
	* <br/>
	* - Parse Groups into SceneObjects <br/>
	* - Parse Lines, Triangles, Quads and Polygons <br/>
	* - UV Mapping <br/>
	*/
	public class OBJLoader extends SceneLoader 
	{
		public function OBJLoader () {}
		
		/** 
		* Object with surfaces for the polygons in the 3D file. <br/> 
		* The surfaces should be assigned before the file is loaded. <br/>
		* 
		* <code><pre>
		* 	import we3d.loader.OBJLoader;
		* 	ldr = new OBJLoader();
		* 	ldr.loadedSurfaces["Default"] = new Surface();
		* 	ldr.loadedSurfaces["Wall"] = new SurfaceTexture();
		* 	ldr.loadFile("myobjfile.obj");
		* </pre></code>
		*/
		public var loadedSurfaces:Object = {};
		
		private var piid:int=-1;
		private var uvs:Array;
		private var allpoints:Array;
		private var tf:TF;
		private var currentLine:int;
		
		public override function parseFile (bytes:ByteArray) :void {
			tf = new TF()
			tf.setFile(bytes.toString());
			
			super.init();
			
			status = 0;
			fileObjects.push(new SceneObject());
			currObject = fileObjects[0];
			uvs = [];
			allpoints = [];
			currentLine = 0;
			
			if(blocking) {
				var L:int = tf._file.length;
				for(var i:int=0; i<L; i++) parseNextLine(tf._file[i]);
				finishParse();
			}else{
				if(piid != -1) clearInterval(piid);
				piid = setInterval(parseStep, loadParseInterval);
			}
		}
		
		private function parseStep () :void {
			var L:int = tf._file.length;
			status = int((currentLine/L) * 100);
			for(var i:int=0; i<linesPerFrame; i++) {
				if(currentLine < L) {
					parseNextLine(clearWhite(tf._file[currentLine]));
					currentLine++;
				}else{
					finishParse();
					break;
				}
			}
		}
		
		private function parseNextLine (lin:String) :void {
			var c1:String = lin.charAt(0);
			
			if( c1 == "#" || lin.length < 3) {
				return;
			}
			
			var c2:String = lin.charAt(1).toLowerCase();
			var str:String;
			var id:int;
			var fc:Face;
			var j:int;
			var i:int;
			var ouv:Array;
			var p:Array;
			var n:String;
			var n2:String;
			var str1:String;
			var uvid:Number;
			
			if(c2 == " ") {
				if(c1 == "g") {
					if(fileObjects.length == 1 && currObject.points.length == 0) {
						return;
					}else{
						fileObjects.push(new SceneObject());
						var name:String = lin.substring(3, lin.length);
						objectsByName[name] = fileObjects[fileObjects.length-1];
						currObject = fileObjects[fileObjects.length-1];
					}
				}
				else if(c1 == "v") {
					n2 = lin.substring(2, lin.length);
					p = n2.split(" ");
					allpoints.push([Number(p[0])*scaleX, Number(p[1])*scaleY, Number(p[2])*scaleZ]);
				}
				else if(c1 == "f" || c1 == "l") {
					
					n2 = lin.substring(2, lin.length);
					p = n2.split(" ");
					
					if(p.length > 0) {
						ouv = new Array();
						
						for(i=0; i<p.length; i++) {
							if(isNaN(Number(p[i]))) {
								str1 = p[i];
								for(j=0; j<str1.length; j++) {
									if(str1.charAt(j) == "/") {
										
										uvid = Number(str1.substring(j+1, str1.length));
										if(uvid < 0) {
											ouv[i] = uvs[uvs.length + uvid];
										}else{
											ouv[i] = uvs[uvid];
										}
										
										uvid = Number(str1.substring(0,j));
										if(uvid < 0) {
											p[i] = allpoints.length + uvid;
										}else{
											p[i] = uvid-1;
										}
									}else if(str1.charAt(j) != "-" && str1.charAt(j) != "e" && isNaN(Number(str1.charAt(j)))) {
										
										uvid = Number(str1.substring(0, j));
										if(uvid < 0) {
											p[i] = allpoints.length + uvid;
										}else{
											p[i] = uvid-1;
										}
										break;
									}
								}
							}
							else{
								p[i] = Number(p[i])-1;	
							}
						}
						
						if(p != null && p.length > 1) {
							var nvts:Array = [];
							var pt:Array;
							for(i=0; i<p.length; i++) {
								pt = allpoints[p[i]];
								nvts.push(currObject.addPoint(pt[0], pt[1], pt[2], true));
							}
							
							if(flipped) {
								fc = currObject.addPolygon(currSurface, nvts.reverse());
								if(ouv.length > 1) {
									for(i=ouv.length-1; i >= 0; i--) {
										fc.addUvCoord(ouv[i].x, ouv[i].y);
									}
								}
							}
							else{
								fc = currObject.addPolygon(currSurface, nvts);
								if(ouv.length > 1) {
									for(i=0; i<ouv.length; i++) {
										fc.addUvCoord(ouv[i].x, ouv[i].y);
									}
								}
							}
						}
					}
				}
			}
			else if(c1 == "u" && c2 == "s") {
				if(lin.substring(0,6) == "usemtl") {
					n = lin.substring(7, lin.length);
					if(loadedSurfaces[n] == undefined) {
						loadedSurfaces[n] = new Surface();
					}
					currSurface = loadedSurfaces[n];
				}
			}
			else if(c1+c2 == "vt") {
				str = lin.substring(3, lin.length);
				p = str.split(" ");
				uvs.push({x:Number(p[0]), y:Number(p[1])});
			}
		}
		
		private function clearWhite (e:String) :String {
			var str:String = e.charAt(e.length-1);
			for(var i:Number=e.length-2; i>=0; i--) {
				if(e.charCodeAt(i) > 32 || e.charCodeAt(i+1) > 32) {
					str = e.charAt(i) + str;
				}
			}
			return str;
		}
		
		private function finishParse () :void {
			clearInterval(piid);
			piid = -1;
			
			dispatchEvent(new Event(Event.COMPLETE));
			
			uvs = null;
			allpoints = null;
			loadedSurfaces = null;
			tf.clear();
			clearMemory();
		}
		
	}
}